﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using System.Management;
using System.Management.Instrumentation;
public class DiskID 
{
    private const string KERNEL = "kernel32.dll";
    public enum DriveCommand : uint
    {
        GetVersion = 0x00074080,
        SendDriveCommand = 0x0007c084,
        ReceiveDriveData = 0x0007c088
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DriverStatus
    {
        public byte DriverError;
        public byte IDEError;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public byte[] Reserved;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DriveIdentifyResult
    {
        public uint BufferSize;
        public DriverStatus DriverStatus;
        public Identify Identify;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct Identify
    {
        public ushort GeneralConfiguration;
        public ushort NumberOfCylinders;
        public ushort Reserved;
        public ushort NumberOfHeads;
        public ushort UnformattedBytesPerTrack;
        public ushort UnformattedBytesPerSector;
        public ushort SectorsPerTrack;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public ushort[] VendorUnique;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] SerialNumber;
        public ushort BufferType;
        public ushort BufferSectorSize;
        public ushort NumberOfEccBytes;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] FirmwareRevision;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
        public byte[] ModelNumber;
        public ushort MoreVendorUnique;
        public ushort DoubleWordIo;
        public ushort Capabilities;
        public ushort MoreReserved;
        public ushort PioCycleTimingMode;
        public ushort DmaCycleTimingMode;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 406)]
        public byte[] More;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DriveCommandParameter
    {
        public uint BufferSize;
        public CommandBlockRegisters Registers;
        public byte DriveNumber;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] Reserved;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct CommandBlockRegisters
    {
        public RegisterFeature Features;
        public byte SectorCount;
        public byte LBALow;
        public byte LBAMid;
        public byte LBAHigh;
        public byte Device;
        public RegisterCommand Command;
        public byte Reserved;
    }
    public enum RegisterFeature : byte
    {
        /// <summary>
        /// Read SMART data.
        /// </summary>
        SmartReadData = 0xD0,
        /// <summary>
        /// Read SMART thresholds.
        /// </summary>
        SmartReadThresholds = 0xD1, /* obsolete */
        /// <summary>
        /// Autosave SMART data.
        /// </summary>
        SmartAutosave = 0xD2,
        /// <summary>
        /// Save SMART attributes.
        /// </summary>
        SmartSaveAttr = 0xD3,
        /// <summary>
        /// Set SMART to offline immediately.
        /// </summary>
        SmartImmediateOffline = 0xD4,
        /// <summary>
        /// Read SMART log.
        /// </summary>
        SmartReadLog = 0xD5,
        /// <summary>
        /// Write SMART log.
        /// </summary>
        SmartWriteLog = 0xD6,
        /// <summary>
        /// Write SMART thresholds.
        /// </summary>
        SmartWriteThresholds = 0xD7, /* obsolete */
        /// <summary>
        /// Enable SMART.
        /// </summary>
        SmartEnableOperations = 0xD8,
        /// <summary>
        /// Disable SMART.
        /// </summary>
        SmartDisableOperations = 0xD9,
        /// <summary>
        /// Get SMART status.
        /// </summary>
        SmartStatus = 0xDA,
        /// <summary>
        /// Set SMART to offline automatically.
        /// </summary>
        SmartAutoOffline = 0xDB, /* obsolete */
    }
    public enum RegisterCommand : byte
    {
        /// <summary>
        /// SMART data requested.
        /// </summary>
        SmartCmd = 0xB0,
        /// <summary>
        /// Identify data is requested.
        /// </summary>
        IdCmd = 0xEC,
    }


    [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)]
    [return: MarshalAsAttribute(UnmanagedType.Bool)]
    public static extern bool DeviceIoControl(IntPtr handle,
  DriveCommand command, ref DriveCommandParameter parameter,
  int parameterSize, out DriveIdentifyResult result, int resultSize,
  out uint bytesReturned, IntPtr overlapped);


    [Flags]
    public enum AccessMode : uint
    {
        Read = 0x80000000,
        Write = 0x40000000,
        Execute = 0x20000000,
        All = 0x10000000
    }
    [Flags]
    public enum ShareMode : uint
    {
        None = 0,
        Read = 1,
        Write = 2,
        Delete = 4
    }
    public enum CreationMode : uint
    {
        New = 1,
        CreateAlways = 2,
        OpenExisting = 3,
        OpenAlways = 4,
        TruncateExisting = 5
    }
    [Flags]
    public enum FileAttribute : uint
    {
        Readonly = 0x00000001,
        Hidden = 0x00000002,
        System = 0x00000004,
        Directory = 0x00000010,
        Archive = 0x00000020,
        Device = 0x00000040,
        Normal = 0x00000080,
        Temporary = 0x00000100,
        SparseFile = 0x00000200,
        ReparsePoint = 0x00000400,
        Compressed = 0x00000800,
        Offline = 0x00001000,
        NotContentIndexed = 0x00002000,
        Encrypted = 0x00004000,
    }
    [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi,
  CharSet = CharSet.Unicode)]
    public static extern IntPtr CreateFile(string fileName,
  AccessMode desiredAccess, ShareMode shareMode, IntPtr securityAttributes,
  CreationMode creationDisposition, FileAttribute flagsAndAttributes,
  IntPtr templateFilehandle);

    private const int MAX_DRIVES = 32;
    public IntPtr InvalidHandle
    {
        get
        {
            return (IntPtr)(-1);
        }
    }
      void Start()
    {
        // Debug.Log("C盘卷标号:" + GetDiskVolume("C"));
    }
    void inits()
    {


        // 取得设备硬盘的物理序列号
        for (int driveIndex = 0; driveIndex < MAX_DRIVES; driveIndex++)
        {
            IntPtr deviceHandle = OpenDrive(driveIndex);
            Debug.Log("disk s:" + deviceHandle);
            if (deviceHandle == InvalidHandle)
                continue;

            DriveCommandParameter parameter = new DriveCommandParameter();
            DriveIdentifyResult result;
            uint bytesReturned;

            parameter.DriveNumber = (byte)driveIndex;
            parameter.Registers.Command = RegisterCommand.IdCmd;

            bool valid = DeviceIoControl(deviceHandle,
              DriveCommand.ReceiveDriveData, ref parameter, Marshal.SizeOf(parameter),
              out result, Marshal.SizeOf(typeof(DriveIdentifyResult)),
              out bytesReturned, IntPtr.Zero);

            if (!valid)
            {
                break;
            }

            string hddName = GetString(result.Identify.ModelNumber);
            string firmwareRevision = GetString(result.Identify.FirmwareRevision);
            Debug.Log("硬盘编号:" + hddName);
            Debug.Log("firmwareRevision:" + firmwareRevision);
        }


        // 取得设备硬盘的卷标号  此方法为取硬盘逻辑分区序列号，重新格式化会改变   
        Debug.Log("C盘卷标号:" + GetDiskVolume("C"));
        

    } 

    public IntPtr OpenDrive(int driveNumber)
    {
        return CreateFile(@"\\.\PhysicalDrive" + driveNumber,
          AccessMode.Read | AccessMode.Write, ShareMode.Read | ShareMode.Write,
          IntPtr.Zero, CreationMode.OpenExisting, FileAttribute.Device,
          IntPtr.Zero);
    }

    private string GetString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length];
        for (int i = 0; i < bytes.Length; i += 2)
        {
            chars[i] = (char)bytes[i + 1];
            chars[i + 1] = (char)bytes[i];
        }
        return new string(chars).Trim(new char[] { ' ', '\0' });
    }

    #region 获取硬盘序列号 
    [DllImport("kernel32.dll")]
    private static extern int GetVolumeInformation(
     string lpRootPathName,
     string lpVolumeNameBuffer,
     int nVolumeNameSize,
     ref int lpVolumeSerialNumber,
     int lpMaximumComponentLength,
     int lpFileSystemFlags,
     string lpFileSystemNameBuffer,
     int nFileSystemNameSize
     );
    /// <summary>
    /// 获取硬盘序列号
    /// </summary>
    /// <param name="drvID">硬盘盘符[c|d|e|....]</param>
    /// <returns></returns>
    public static string GetDiskVolume(string drvID)
    {
        const int MAX_FILENAME_LEN = 256;
        int retVal = 0;
        int lpMaximumComponentLength = 0;
        int lpFileSystemFlags = 0;
        string lpVolumeNameBuffer = null;
        string lpFileSystemNameBuffer = null;
        int i = GetVolumeInformation(
        drvID + @":\",
        lpVolumeNameBuffer,
        MAX_FILENAME_LEN,
        ref retVal,
        lpMaximumComponentLength,
        lpFileSystemFlags,
        lpFileSystemNameBuffer,
        MAX_FILENAME_LEN
        );

        return retVal.ToString("x");
    }
    #endregion
}